<?php 
/*
 * This code is distributed and used under the GNU GPL v2 License,
 * please, read file LICENSE, which is situated in root directory,
 * or in "LICENSE" directory with most acceptable translation for you
 *
 * Basic core,modules,library objects abstract layer
 *
 * Copyright (C) 2010, Taras Kudla
 *
 */


/**
 * @abstract
 * 
 */
abstract class gKernel
{

    /**
     *
     * @var ginit   Gepard core object
     */
    protected   $core   = null;


    private     $adapters = array();
    /**
    private     $loaded = false;
     * Kernel object constructor, based on some php hacks
     *
     * @param ginit $Gepard     Link to preinitialized Gepard object
     */
    function __construct(gInit $Gepard){

        $this->core = $Gepard;
    }

    /**
     * Gepard kernel object initialization method
     * Use it instead of __construct()
     */
    public function init()
    {
        
    }
    
    public function getAdapter($name,$interface=null)
    {
        if( !array_key_exists($name, $this->adapters))
        {
            $objname = strtolower(substr(get_class($this),1,strlen(get_class($this))));
            $filename = $this->core->corepath().'adapters/'.$objname.'/'.$name.'.php';
            if( !file_exists($filename))
            {
				throw new gException("Cannot find adapter file '$filename'");
            }
            require_once $filename;
            if( $interface!=null && !($classname instanceof $interface) )
            {
                throw new gException('Adapter '.$name.' is not supported by interface');
            }
            $classname = get_class($this) .$name;
            $this->adapters[$name] = new $classname($this->core);
        }
        return $this->adapters[$name];
    }

    public function getAdaptersPool()
    {
        return $this->adapters;
    }

    function __clone()
    {
        
    }



    /**
     * Do nothing
     *
     * @param string $string
     * @return mixed
     */
    public function void($string = null){
        if( $string!=null){
            return $string;
        } else {
            return get_class($this);
        }
    }
    
}

abstract  class gController extends gKernel {
    
}

abstract class gModule {

    /**
     * Gateway to Gepard kernel
     *
     * @var ginit
     */
    protected $api = null;
    /**
     * Module i18n language object
     *
     * @var gi18n_image
     */
    protected $lang = null;
    protected $name = null;
    protected $i18n = null;
    protected $langfilespath = null;
    protected $requestmethod = null;

    /**
     * Module initialization
     *
     * @param ginit $Gepard 
     */
    function __construct(gInit $Gepard){
        $this-> api = $Gepard;
        $env = $this->api->Getenvcaller();
        $module=$env["MOD"];
        $this->name = $env["MOD"];
        $this->requestmethod = $this->api->document->gethttprequestmethod();
        $this->lang=$this->api->i18n->setModuleLanguage($module, $this->api->GetSysLanguages(), $this->api->GetLanguage(),$this->langfilespath);
        $this->i18n = & $this->lang;

    }

    /**
     * Module initialization method
     * Use it instead of __construct()
     *
     */
    public function init(){
    }

    /**
     * Module defaultaction
     *
     * @see moduel default action
     */
    public function defaultaction(){

    }
	
    /**
     * Returns url to module path
     *
     * @return string
     */
    public function getmyurl(){
        return $this->api->url()."mod/".$this->name."/";
    }
    
    /**
     * Returns path to module
     *
     * @return string
     */
    public function getmypath(){
        return $this->api->path()."mod/".$this->name."/";
    }

    /**
     * Module installation method
     */
    public function install(){

    }

    /**
     * Module uninstallation method
     *
     */
    public function uninstall(){
    
    }

    /**
     *
     */
    function __clone(){

    }

    /**
     * Do nothing
     */
    public function void(){
        
    }
}


abstract class module extends gModule {
    
}

/*
 * Library layer
 */
abstract class library extends gLibrary {

}

abstract  class gLibrary {

	protected $api = null;
	protected $lang = null;

	function __construct(gInit $Gepard){
            $this-> api =$Gepard;
	}
	public function init(){
	# library object initialization code

        }
}
/*
 * Kernel objects calls dispatcher,
 * NOTE: on works noly with call methods,
 * on __get it returns kernel object
 */

class gdispatcher {
    /**
     *
     * @var <type>
     */
    private $core = null;
    /**
     *
     * @var <type>
     */
    private $statecaller = null;
    /**
     *
     * @var <type>
     */
    private $stack = null;
    /**
     *
     * @var <type> 
     */
    private $laststatecaller = null;
    /**
     *
     * @var <type>
     */
    private $tracestack = array();

    /**
     * Gepard object binding
     *
     * @param ginit $Gepard
     */
    function __construct(gInit $Gepard){
        $this->core = $Gepard;
    }
    /**
     * Setting current execution source
     *
     * @param string $callername
     */
    public function setstatecaller($callername){
        $this->laststatecaller = $this->statecaller;
        $this->statecaller=$callername;
    }
    /**
     * Returns all call from tracing stack
     *
     * @return <type> 
     */
    public function getdispatchtrace(){
        return $this->tracestack;
    }
    /*
     * TODO: Stack implementation needed
     */
    /**
     *
     * @param <type> $name
     * @param <type> $params
     * @return <type>
     */
    function __call($name,$params){
        /*
         * Memory usage control
         */
        if( memory_get_usage() > $this->core->get("memorylimit")){
            $this->core->error("dispatcher","Allowed memory size is overflow",_ERR_CRIT,__FILE__,__LINE__);
            return false;
        }
        
        $time = microtime(1);
        $obj = $this->core->getObject($this->statecaller);
        $objname = $this->statecaller;
        $this->core->calltrigger(_TRIG_CALL, _TRIG_ONBEFORE,array("object"=>$this->statecaller,"method"=>$name));
        if( !method_exists($obj, $name)){
            $this->core->error("dispatcher","Cannot find method '$name' for object '".get_class($obj)."'",_ERR_FATAL);
            return false;
        }
        $calltype = call_user_func_array(array(&$obj,$name), $params);
        $this->core->calltrigger(_TRIG_CALL, _TRIG_ONAFTER,array("object"=>$this->statecaller,"method"=>$name));
        $this->statecaller = $this->laststatecaller;
        $time = round(microtime(1)-$time,5);
        $this->tracestack[]=array("object"=>$objname,"method"=>$name,"time"=>$time,"kernelstate"=>$this->core->getExecutionState());
        return $calltype;
    }
    /**
     *
     * @param <type> $field
     * @return <type> 
     */
    function __get($field){
        $obj = $this->core->getObject($this->statecaller);
        return $obj->$field;
    }
}
/*
 * Interfaces
 */
 
# templates engines
interface gTemplateEngineStructure
{
    public function bindvar($varname,$varvalue);
    public function deletevar($varname);
    public function setvars( $vars );
    public function render($text,$data=null,$i18n=null);
} 
# database objects interface
interface gDatabaseConnector
{
    public function select($table,$fields="*",$sql=null);
    public function insert($table,array $data,$returnid=null);
    public function update($table,$sql);
    public function delete($table,$sql);
    public function execute($sql);
    public function connect($serv,$port,$db,$user,$passwd);
    public function disconnect();
    public function version();
    public function createtable(sqltable $map,$returnsql=false);
    public function droptable($table);
    public function tableexists($table);
    public function getfields($table);
    public function gettables($database=null);
    public function getdatabases();
    public function getdatabase();
}
# caching engine interface
interface gCacheAdapter
{
    public function connect($server,$port);
    public function set($key,&$value);
    public function get($key);
    public function delete($key);
    public function disconnect();
    public function flush();
    public function stats();
}

interface gLinkerStructure
{
    
}


interface gSessionStructure
{
    public function start();
    public function close();
    public function clear($sessionId);
    public function destroy();
    public function write();
    public function read();
    public function set($key,$value=null);
    public function get($key);
    public function delete($key);
    public function exists($key);
    public function collectgarbage();
    public function resetlifetime($time);
}


/*
 *
 *
 *
 */
class sqlQuery {

        private $query = '';
        private $whereIsSet = false;
        private $contype = null;
        private $stack = array();

        function __construct($contype=null){

        }
        function __get($name){
            $this->stack[]=array($name=>null);
            return $this;
        }

        function __call($name,$options){
            if( array($options)){
                $options = implode(" ",$options);
            }
            $this->stack[] = array($name=>$options);
            return $this;
        }

        /*
         * AND, OR, NOT, IN
         */

        public function _and() {
            $this->stack[] =array("direct"=>' AND ');
            return $this;
        }

        public function _or() {
            $this->stack[] = array("direct"=>' OR ');
            return $this;
        }

        public function _not(){
            $this->stack[] = array("direct"=>' NOT ');
            return $this;
        }
        public function _in() {
            $this->stack[] = array("direct"=>' IN ');
            return $this;

        }

        /*
         * Collumn name, value, tablename
         */
        public function collumn($name){
            $this->stack[]=array("collumn"=>$name);
            return $this;
        }

        /*
         * SELECT, UPDATE, DELETE, FROM, WHERE, LIMIT, LIKE, ORDER, HAVING, GROUP
         */
        public function select($columns='*') {
            $this->stack[]=array("select"=>$columns);
            return $this;
        }

        public function update($tablename){
            $this->stack[]=array("update"=>$tablename);
            return $this;
        }
        public function delete($tablename){
            $this->stack[]=array("delete"=>$tablename);
            return $this;
        }

        public function from($tables) {
           $this->stack[]=array("from"=>$tables);
           return $this;
        }

        public function where($collumn,$condition=null,$value=null) {
            $this->stack[]=array("where"=>null);
            $this->stack[]=array("collumn"=>$collumn);
            if( $condition!=null)
                $this->stack[]=array("direct"=>$condition);
            if( $value!=null){
                $this->stack[]=array("value"=>$value);
            }
            return $this;
        }

        public function like($cond) {
            $this->stack[]=array("direct"=>'LIKE '.$cond);
            return $this;
        }

        public function limit($amount, $start=null) {
            $this->stack[]=array("direct"=>'LIMIT '.$amount.' '.($start===null ? '': ','.$start.' '));
            return $this;
        }

        public function order($columns, $type=null) {
            $this->stack[]=array("order"=>$columns);
            if( $type!=null){
                $this->stack[]=array($type=>null);
            }
            return $this;
        }

        /*
         * Service area
         */
         public function create(){
             return $this->getQuery();
         }

         public function getQuery() {
            /*
             * preparing stack as query
             */
            $query = '';
            for($i=0;$i<count($this->stack);$i++)
            foreach( $this->stack[$i] as $k=>&$v){
                # checking for select
                if( $k=="select"){
                    $cols = '';
                    if(is_array($v)) {
                        foreach($v as $synonim => $column) {
                            if(!is_numeric($synonim))
                                $cols.=' `'.$synonim.'` as '.$column;
                            else
                                $cols.=' `'.$column.'` ';
                            $cols.=',';
                        }
                        $cols=substr($cols, 0, -1);

                } else $cols = $v;
                $this->whereIsSet = false;
                $query.='SELECT '.$cols.' ';
                }
                else if( $k=="update"){
                    $table = $v;
                    if( is_array($table)){
                        $table = implode(",",$table);
                    }
                    $query.='UPDATE '.$table.' ';
                }
                else if( $k=="DELETE"){
                    $table = $v;
                    if( is_array($table)){
                        $table = implode(",",$table);
                    }
                    $query.='DELETE FROM '.$table.' ';
                }
                else if( $k=="from"){
                   $tbls = '';
                    if(is_array($v)) {
                        foreach($v as $synonim => $table) {
                             if(!is_numeric($synonim))
                                $tbls.=' `'.$synonim.'` '.$table;
                            else
                                $tbls.=' `'.$table.'` ';
                            $tbls.=',';
                        }
                        $tbls=substr($tbls, 0, -1);

                    } else
                        $tbls = $v;
                $query.=' FROM '.$tbls.' ';
                }

                else if( $k=="direct"){
                    $query.=' '.$v.' ';
                }
                else if( $k=="value"){
                    $query.=' \''.$v.'\' ';
                }
                else if( $k=="collumn" || $k=="field"){
                    $query.=' '.$v.' ';
                }
                else if( $k=="where"){
                     if(!$this->whereIsSet){
                         $query.=' WHERE ';
                         $this->whereIsSet=true;
                     }
                }
                else if( $k=="order"){
                    $cols =$v;
                    if(is_array($v)){
                        $cols=implode(',', $columns);
                    }
                    $query.='ORDER BY '.$cols.' ';
                }
                else {
                    $query.=' '.$k.' '.$v;
                }

            }

            $this->clearAll();
            return $query;
      }

       public function clearAll() {
            $this->stack = array();
            return $this;
       }

       public function __toString() {
            return $this->getQuery();
       }
}
